﻿namespace Sustainalytics.LongRunningTaskManager.Service
{
    using System;
    using System.Collections.Generic;
    using System.Net;
    using System.Net.Http;
    using System.Threading.Tasks;
    using System.Web.Http;
    using System.Web.Http.Filters;

    using Microsoft.AspNet.SignalR;
    using Microsoft.Owin;

    using Newtonsoft.Json;

    using Owin;

    using Sustainalytics.LongRunningTaskManager.Interface;
    using Sustainalytics.Utils;
    using Sustainalytics.Utils.SignalR;
    using Microsoft.Owin.Cors;

    public class Startup
    {
        public void Configuration(IAppBuilder builder)
        {

            builder.Use<OwinExceptionMiddleware>();
            // load all Sustainalytics assemblies
            AssemblyUtility.LoadAssembliesFromBin("Sustainalytics.*.dll");

            builder.UseAuthenticationServer();           

            var config = new HttpConfiguration();
            config.Filters.Add(new LRTMExceptionFilterAttribute());

            WebApiConfig.Register(config);

            builder.UseCors(CorsOptions.AllowAll);

            config.Formatters.XmlFormatter.SupportedMediaTypes.Clear();

            var hubConfiguration = new HubConfiguration
            {
                // You can enable JSONP by uncommenting line below.
                // JSONP requests are insecure but some older browsers (and some
                // versions of IE) require JSONP to work cross domain
                //EnableJSONP = true,
                // Resolver = new CamelCasePropertyNamesContractResolver()
            };
            builder.MapSignalR(hubConfiguration);

            builder.UseWebApi(config);

            config.EnsureInitialized();

            var settings = new JsonSerializerSettings { ContractResolver = new SignalRCamelCaseContractResolver() };
            var serializer = JsonSerializer.Create(settings);

            GlobalHost.DependencyResolver.Register(typeof(JsonSerializer), () => serializer);

            GlobalHost.Configuration.DefaultMessageBufferSize = 32;
                                    

            ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
        }
    }

    public class OwinExceptionMiddleware : OwinMiddleware
    {
        public OwinExceptionMiddleware(OwinMiddleware next)
            : base(next)
        {
        }

        public override async Task Invoke(IOwinContext context)
        {
            try
            {
                await this.Next.Invoke(context).ConfigureAwait(false);
            }
            catch (Exception ex)
            {

                LongRunningTaskManagerEventSource.Log.TaskManagerInitError(ex);

                this.HandleException(ex, context);
            }
        }

        private void HandleException(Exception ex, IOwinContext context)
        {
            var request = context.Request;

            //Build a model to represet the error for the client
            var errorDataModel = new { ErrorCode = 1010 };
            context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
            context.Response.ReasonPhrase = "Internal Server Error";
            context.Response.ContentType = "application/json";
            context.Response.Write(JsonConvert.SerializeObject(errorDataModel));
        }
    }

    public class LRTMExceptionFilterAttribute : ExceptionFilterAttribute
    {

        private readonly Dictionary<Type, HttpStatusCode> _knownExceptionsDictionary = new Dictionary<Type, HttpStatusCode>
                                                                                 {
                                                                                     { typeof(LongRunningTaskInstanceNotFoundException), HttpStatusCode.NotFound },
                                                                                     { typeof(LongRunningTaskManagerStoppingException), HttpStatusCode.BadRequest },
                                                                                     { typeof(LongRunningMaxTasksPerUserReachedException), HttpStatusCode.BadRequest },
                                                                                     { typeof(LongRunningTaskManagerAuthenticationTicketNullException), HttpStatusCode.BadRequest },
                                                                                     { typeof(LongRunningTaskInstanceIdNullOrEmptyException), HttpStatusCode.BadRequest },
                                                                                     { typeof(LongRunningTaskInstanceDictionaryRemoveException), HttpStatusCode.BadRequest },
                                                                                     { typeof(PostTaskDataInvalidException), HttpStatusCode.BadRequest },
                                                                                 }; 

        public override void OnException(HttpActionExecutedContext actionExecutedContext)
        {
            HttpStatusCode information;
            if (this._knownExceptionsDictionary.TryGetValue(actionExecutedContext.Exception.GetType(), out information))
            {
                actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(
                   information,
                  actionExecutedContext.Exception.GetHttpResponseMessageDto());
            }

            LongRunningTaskManagerEventSource.Log.TaskManagerInitError(actionExecutedContext.Exception);

            actionExecutedContext.SetErrorResponseFor<Exception>(HttpStatusCode.InternalServerError, actionExecutedContext.Exception.ToString());
        }
    }

    public static class HttpActionExecutionContextExtensions
    {
        public static HttpActionExecutedContext SetErrorResponseFor<T>(
            this HttpActionExecutedContext context,
            HttpStatusCode statusCode,
            object message = null)
        {
            if (context.Response == null && context.Exception is T)
            {
                context.Response = message != null
                                       ? context.Request.CreateResponse(statusCode, message)
                                       : context.Request.CreateResponse(statusCode);
            }

            return context;
        }
    }
}
